package com.ellfors.module_first.index_rcv.widget

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.widget.TextView
import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.OnScrollListener
import com.ellfors.common.ext.dp2px
import com.ellfors.common.ext.sp2px
import kotlin.math.max
import kotlin.math.min

/**
 * 索引右侧边栏
 */
class IndexBar : View {

    //是否需要根据实际的数据来生成索引数据源（例如 只有 A B C 三种tag，那么索引栏就 A B C 三项）
    private var isNeedRealIndex = false

    //索引数据源
    private var mIndexData: MutableList<String>? = null

    //View的宽高
    private var mWidth = 0
    private var mHeight = 0

    //每个index区域的高度
    private var mGapHeight = 0

    private var mBackgroundPaint: Paint? = null
    private var mTextPaint: Paint? = null

    private var mTextSize = 9.sp2px()
    private var mTextNormalColor = Color.parseColor("#292A33")
    private var mTextSelectedColor = Color.parseColor("#FFFFFF")
    private var mBackgroundSelectedColor = Color.parseColor("#292A33")
    private var mBackgroundCircleRadius = 6.dp2px().toFloat()

    //手指按下时的背景色
    private var mPressedBackground = Color.TRANSPARENT

    //以下边变量是外部set进来的
    private var mPressedShowTextView: TextView? = null //用于特写显示正在被触摸的index值
    private var isSourceDataAlreadySorted = false //源数据 已经有序？
    private var mSourceData: List<IndexTag?>? = null //Adapter的数据源
    private var mRecyclerView: RecyclerView? = null
    private var headerViewCount = 0
    private var mCurrentSelectedTag: String? = null

    private var mOnIndexPressedListener: IndexPressedListener? = null

    constructor(context: Context?) : super(context)
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context, attrs, defStyleAttr
    )

    init {
        initIndexData()

        mTextPaint = Paint().apply {
            isAntiAlias = true
            textSize = mTextSize.toFloat()
            color = mTextNormalColor
        }
        mBackgroundPaint = Paint().apply {
            isAntiAlias = true
            color = mBackgroundSelectedColor
        }

        //设置index触摸监听器
        setOnIndexPressedListener(object : IndexPressedListener {
            override fun onIndexPressed(index: Int, text: String) {
                mPressedShowTextView?.isVisible = true
                mPressedShowTextView?.text = text
                //滑动Rv
                mRecyclerView?.also {
                    val position = getPosByTag(text)
                    if (position != -1) {
                        (it.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(
                            position, 0
                        )
                    }
                }
            }

            override fun onMotionEventEnd() {
                //隐藏hintTextView
                mPressedShowTextView?.isVisible = false
            }
        })
    }

    @SuppressLint("DrawAllocation")
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        //取出宽高的MeasureSpec  Mode 和Size
        val wMode = MeasureSpec.getMode(widthMeasureSpec)
        val wSize = MeasureSpec.getSize(widthMeasureSpec)
        val hMode = MeasureSpec.getMode(heightMeasureSpec)
        val hSize = MeasureSpec.getSize(heightMeasureSpec)
        var measureWidth = 0
        var measureHeight = 0 //最终测量出来的宽高

        //得到合适宽度：
        val indexBounds = Rect() //存放每个绘制的index的Rect区域

        mIndexData?.forEach { index ->
            //测量计算文字所在矩形，可以得到宽高
            mTextPaint?.getTextBounds(index, 0, index.length, indexBounds)
            //循环结束后，得到index的最大宽度
            measureWidth = max(indexBounds.width(), measureWidth)
            //循环结束后，得到index的最大高度，然后*size
            measureHeight = max(indexBounds.height(), measureHeight)
        }
        mIndexData?.also {
            measureHeight *= it.size
        }

        when (wMode) {
            MeasureSpec.EXACTLY -> measureWidth = wSize
            //wSize此时是父控件能给子View分配的最大空间
            MeasureSpec.AT_MOST -> measureWidth =
                min(measureWidth.toDouble(), wSize.toDouble()).toInt()
            MeasureSpec.UNSPECIFIED -> {}
        }
        when (hMode) {
            MeasureSpec.EXACTLY -> measureHeight = hSize
            //wSize此时是父控件能给子View分配的最大空间
            MeasureSpec.AT_MOST -> measureHeight =
                min(measureHeight.toDouble(), hSize.toDouble()).toInt()
            MeasureSpec.UNSPECIFIED -> {}
        }
        setMeasuredDimension(measureWidth, measureHeight)
    }

    @SuppressLint("DrawAllocation")
    override fun onDraw(canvas: Canvas) {
        mIndexData?.forEachIndexed { pos, tag ->
            //用来计算baseLine。因为drawText的y坐标，代表的是绘制的文字的baseLine的位置
            val baseline = mTextPaint?.let {
                //获得画笔的FontMetrics
                val fontMetrics = it.fontMetrics
                ((mGapHeight - fontMetrics.bottom - fontMetrics.top) / 2).toInt()
            } ?: 0

            val isCurrentSelected = tag == mCurrentSelectedTag
            //画背景
            if (isCurrentSelected) {
                mBackgroundPaint?.also {
                    canvas.drawCircle(
                        (mWidth / 2).toFloat(),
                        ((mGapHeight * pos) + mGapHeight / 2).toFloat(),
                        mBackgroundCircleRadius,
                        it
                    )
                }
            }
            //计算出在每格index区域，竖直居中的baseLine值
            mTextPaint?.also {
                it.color = if (isCurrentSelected) {
                    mTextSelectedColor
                } else {
                    mTextNormalColor
                }
                //调用drawText，居中显示绘制index
                canvas.drawText(
                    tag,
                    mWidth / 2 - it.measureText(tag) / 2,
                    (paddingTop + mGapHeight * pos + baseline).toFloat(),
                    it
                )
            }
        }
    }

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent): Boolean {
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                //手指按下时背景变色
                setBackgroundColor(mPressedBackground)
                val y = event.y
                //通过计算判断落点在哪个区域：
                var pressI = ((y - paddingTop) / mGapHeight).toInt()
                mIndexData?.also {
                    //边界处理（在手指move时，有可能已经移出边界，防止越界）
                    if (pressI < 0) {
                        pressI = 0
                    } else if (pressI >= it.size) {
                        pressI = it.size - 1
                    }
                    //回调监听器
                    if (pressI > -1 && pressI < it.size) {
                        mOnIndexPressedListener?.onIndexPressed(pressI, it[pressI])
                    }
                }
            }
            MotionEvent.ACTION_MOVE -> {
                val y = event.y
                var pressI = ((y - paddingTop) / mGapHeight).toInt()
                mIndexData?.also {
                    if (pressI < 0) {
                        pressI = 0
                    } else if (pressI >= it.size) {
                        pressI = it.size - 1
                    }
                    if (pressI > -1 && pressI < it.size) {
                        mOnIndexPressedListener?.onIndexPressed(pressI, it[pressI])
                    }
                }
            }
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                //手指抬起时背景恢复透明
                setBackgroundResource(android.R.color.transparent)
                //回调监听器
                mOnIndexPressedListener?.onMotionEventEnd()
            }
            else -> {
                setBackgroundResource(android.R.color.transparent)
                mOnIndexPressedListener?.onMotionEventEnd()
            }
        }
        return true
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        mWidth = w
        mHeight = h
        if (mIndexData.isNullOrEmpty()) {
            return
        }
        computeGapHeight()
    }

    /**
     * 初始化原始数据源，并取出索引数据源
     */
    private fun initSourceData() {
        if (mSourceData.isNullOrEmpty()) {
            return
        }
        if (isNeedRealIndex) {
            getSortedIndexData(mIndexData)
            computeGapHeight()
        }
    }

    private fun getSortedIndexData(indexDatas: MutableList<String>?) {
        if (mSourceData.isNullOrEmpty()) {
            return
        }
        mSourceData?.also {
            //按数据源来 此时sourceData 已经有序
            val size = it.size
            var baseIndexTag: String?
            for (i in 0 until size) {
                baseIndexTag = it[i]?.suspensionTag
                if (indexDatas?.contains(baseIndexTag) == false) {
                    //则判断是否已经将这个索引添加进去，若没有则添加
                    indexDatas.add(baseIndexTag ?: "")
                }
            }
        }
    }

    private fun initIndexData() {
        mIndexData = if (isNeedRealIndex) {
            ArrayList()
        } else {
            INDEX_STRING
        }
    }

    /**
     * 以下情况调用：
     * 1 在数据源改变
     * 2 控件size改变时
     * 计算gapHeight
     */
    private fun computeGapHeight() {
        mGapHeight = (mHeight - paddingTop - paddingBottom) / (mIndexData?.size ?: 0)
    }

    /**
     * 根据传入的tag返回pos
     */
    private fun getPosByTag(tag: String): Int {
        if (mSourceData.isNullOrEmpty() || tag.isEmpty()) {
            return -1
        }
        mSourceData?.forEachIndexed { index, indexTag ->
            if (indexTag?.suspensionTag == tag) {
                return index + headerViewCount
            }
        }
        return -1
    }

    /**
     * 当前被按下的index的监听器
     */
    interface IndexPressedListener {
        //当某个Index被按下
        fun onIndexPressed(index: Int, text: String)

        //当触摸事件结束（UP CANCEL）
        fun onMotionEventEnd()
    }

    fun setOnIndexPressedListener(mOnIndexPressedListener: IndexPressedListener?) {
        this.mOnIndexPressedListener = mOnIndexPressedListener
    }

    /**
     * 显示当前被按下的index的TextView
     */
    fun setPressedShowTextView(mPressedShowTextView: TextView?): IndexBar {
        this.mPressedShowTextView = mPressedShowTextView
        return this
    }

    fun bindRecyclerView(recyclerView: RecyclerView): IndexBar {
        this.mRecyclerView = recyclerView.apply {
            addOnScrollListener(object : OnScrollListener() {
                override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                    super.onScrolled(recyclerView, dx, dy)
                    val firstPos =
                        (layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
                    mSourceData?.get(firstPos)?.also {
                        setCurrentSelectedTag(it.suspensionTag)
                    }
                }
            })
        }
        return this
    }

    /**
     * 一定要在设置数据源[.setSourceData]之前调用
     */
    fun setNeedRealIndex(needRealIndex: Boolean): IndexBar {
        isNeedRealIndex = needRealIndex
        initIndexData()
        return this
    }

    fun setSourceData(data: List<IndexTag?>?): IndexBar {
        this.mSourceData = data
        //对数据源进行初始化
        initSourceData()
        return this
    }

    /**
     * 设置HeaderView的Count
     */
    fun setHeaderViewCount(headerViewCount: Int): IndexBar {
        this.headerViewCount = headerViewCount
        return this
    }

    /**
     * 源数据 是否已经有序
     */
    fun setSourceDataAlreadySorted(sourceDatasAlreadySorted: Boolean): IndexBar {
        isSourceDataAlreadySorted = sourceDatasAlreadySorted
        return this
    }

    fun setCurrentSelectedTag(tag: String): IndexBar {
        if (mCurrentSelectedTag != tag) {
            mCurrentSelectedTag = tag
            invalidate()
        }
        return this
    }

    fun setTextSize(size: Int): IndexBar {
        this.mTextSize = size
        mTextPaint?.textSize = mTextSize.toFloat()
        return this
    }

    fun setTextNormalColor(color: Int): IndexBar {
        this.mTextNormalColor = color
        return this
    }

    fun setTextSelectedColor(color: Int): IndexBar {
        this.mTextSelectedColor = color
        return this
    }

    fun setBackgroundSelectedColor(color: Int): IndexBar {
        this.mBackgroundSelectedColor = color
        return this
    }

    fun setPressedBackground(color: Int): IndexBar {
        this.mPressedBackground = color
        return this
    }

    companion object {
        //默认的数据源
        private val INDEX_STRING = mutableListOf(
            "A",
            "B",
            "C",
            "D",
            "E",
            "F",
            "G",
            "H",
            "I",
            "J",
            "K",
            "L",
            "M",
            "N",
            "O",
            "P",
            "Q",
            "R",
            "S",
            "T",
            "U",
            "V",
            "W",
            "X",
            "Y",
            "Z"
        )
    }
}
